/*
 * 
 * LegendShop 多用户商城系统
 * 
 *  版权所有,并保留所有权利。
 * 
 */
package com.legendshop.permission.service.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;

import com.legendshop.oa.constants.Constants;
import com.legendshop.oa.model.Menu;
import com.legendshop.oa.model.MenuComparator;
import com.legendshop.oa.security.UserDetail;
import com.legendshop.oa.security.UserManager;
import com.legendshop.permission.service.MenuManager;
import com.legendshop.permission.service.MenuManagerService;
import com.legendshop.util.AppUtils;

/**
 * 后台菜单管理器
 */
public class MenuManagerImpl implements MenuManager{

	private static Logger log = LoggerFactory.getLogger(MenuManagerImpl.class);

	private MenuManagerService menuManagerService;

	/**  按照3级菜单来组装. */
	private List<Menu> menus;
	
	// 1. 找到所有在线的菜单
	private Map<Long, Menu> menuMap;

	/** 每个角色所拥有的菜单,格式： roleName: List<Menu> */
	private Map<String, List<Menu>> role2menu;

	private Object lockObj = new Object();

	/**
	 * 初始化菜单
	 */
	public void init() {
		// 1. 找到所有在线的菜单
		List<Menu> menuList = menuManagerService.getMenu();
		log.debug("total menu with size {}", menuList.size());
		
		menuMap = new HashMap<Long, Menu>();
		for (Menu menu : menuList) {
			menuMap.put(menu.getMenuId(), menu);
		}

		// 2. 按照3级菜单来组装
		this.menus = parseMenu(menuList);
		log.debug("parsed menu with size {}", menus.size());

		// 3. 找到每个角色所拥有的菜单
		this.role2menu = menuManagerService.buildRoleMenu(menuList);
		log.debug("built role to menu mapping finished");
	}

	/**
	 * 获取顶级菜单
	 */
	@SuppressWarnings("unchecked")
	public List<Menu> getTopMenus(HttpSession session) {
		List<Menu> menuList = (List<Menu>) session.getAttribute(Constants.MENU_LIST);
		if (menuList == null) {
			//加载菜单
			try {
				menuList = calUserMenus(session);
			} catch (Exception e) {
				session.removeAttribute(Constants.MENU_LIST);
				log.error("Calculate admin menu error ", e );
			}
		}
		return menuList;
	}

	/**
	 * 根据权限，计算用户的菜单树，并缓存到session中
	 * 
	 * @param session
	 */
	@SuppressWarnings({ "unchecked" })
	public List<Menu> calUserMenus(HttpSession session) {

		List<Menu> menuList = (List<Menu>) session.getAttribute(Constants.MENU_LIST);
		if (AppUtils.isBlank(menuList)) {
			synchronized (lockObj) {
				menuList = (List<Menu>) session.getAttribute(Constants.MENU_LIST);
				if (AppUtils.isBlank(menuList)) {
					UserDetail userDetail = UserManager.getUser(session);
					if(userDetail == null) {
						return new ArrayList<Menu>();
					}
					Collection<GrantedAuthority> useRoleList = userDetail.getAuthorities();
					if(AppUtils.isBlank(useRoleList)){
						return menuList;
					}
					
					Iterator<GrantedAuthority> userRole = useRoleList.iterator();

					// 获取可访问的菜单并去重（求并集），因为一个用户可能拥有不同的角色，角色间的访问权限可能有交叉
					Set<Menu> userMenuSet = new HashSet<Menu>();
					while (userRole.hasNext()) {
						GrantedAuthority role = userRole.next();
						List<Menu> ml = role2menu.get(role.getAuthority());
						if (ml != null)
							userMenuSet.addAll(ml);
					}

					// 创建3个桶对应3个级别的菜单，用于存储菜单副本并加快查找，比如：
					// menuBins[0] 为1级菜单，menuBins[1] 为 2 级菜单
					// 查找 2 级菜单的父节点时可以在 1 级菜单桶 menuBins[0] 中查找
					// 从而避免从头到尾遍历所有菜单（如果直接用binarySearch也行）
					@SuppressWarnings("rawtypes")
					List[] menuBins = { new ArrayList<Menu>(), new ArrayList<Menu>(), new ArrayList<Menu>() };

					for (Menu menu : userMenuSet) {
						// 创建一个菜单副本（避免影响原菜单）
						Menu me = menu.copy();
						// 将菜单副本装入不同的桶中
						menuBins[me.getGrade() - 1].add(me);
					}

					// 对菜单进行排序（影响显示顺序）
					for (List<Menu> sorter : menuBins) {
						Collections.sort(sorter, new MenuComparator());
					}

					// 组装一棵菜单树（将子菜单加入父菜单中）
					List<Menu>[] menuTree = menuBins;
					int levels[] = { 1, 2 };
					for (int lv : levels) {
						for (Menu menu : menuTree[lv]) { // 当前菜单层数为 lv
															// 则其对应的父菜单位于 lv-1
							// 查找父节点
							Menu menuPt = null;
							for (Menu pt : menuTree[lv - 1]) {
								if (pt.getMenuId().equals(menu.getParentId())) {
									menuPt = pt;
									break;
								}
							}

							// 异常情况
							if (menuPt == null) {
								log.warn("授权的菜单中，找不到菜单：[" + menu.getName() + "]的父节点菜单，可能是因为父菜单下线。");
								continue;
							}

							// 将当前菜单加入父节点中
							List<Menu> subList = menuPt.getSubList();
							if (subList == null) {
								subList = new ArrayList<Menu>();
								menuPt.setSubList(subList);
							}
							subList.add(menu);
						}
					}
					// 获得顶层菜单
					menuList = menuTree[0];
					
					//将顶层的action设置为他的子节点
					for (Menu menu : menuList) {
						List<Menu> subMenus = menu.getSubList(); //二级菜单
						if(AppUtils.isNotBlank(subMenus)){
							Menu menu3 = subMenus.get(0);
							if(AppUtils.isNotBlank(menu3)){
								List<Menu> subMenus3 = menu3.getSubList();
								if(AppUtils.isNotBlank(subMenus3)){
									menu.setMenuAction(subMenus3.get(0).getAction());
								}
							}
						}
					}
					session.setAttribute(Constants.MENU_LIST, menuList);
				}
			}
		}
		return menuList;
	}

	/**
	 * @param menuManagerService
	 *            the menuManagerService to set
	 */
	public void setMenuManagerService(MenuManagerService menuManagerService) {
		this.menuManagerService = menuManagerService;
	}

	/**
	 * 按照3级菜单来组装
	 * 
	 * @param menuList
	 * @return
	 */
	private List<Menu> parseMenu(Collection<Menu> menuList) {
		Map<Long, Menu> menuMap = new LinkedHashMap<Long, Menu>();
		for (Menu menu : menuList) {
			if (menu.getGrade() == 1) { // for 顶级菜单
				menuMap.put(menu.getMenuId(), menu);
			} else if (menu.getGrade() == 2) { // 二级菜单
				// 拿到一级菜单先
				Menu menuLevel1 = menuMap.get(menu.getParentId());
				menu.setParentMenu(menuLevel1);
				if (menuLevel1 != null) {
					menuLevel1.addSubMenu(menu);
				}
			} else if (menu.getGrade() == 3) { // 三级菜单
				// 拿到二级菜单
				Menu secondMenu = getParentMenu(menuList, menu);
				menu.setParentMenu(secondMenu);
				if (secondMenu != null) {
					// 拿到一级菜单先
					Menu menuLevel1 = menuMap.get(secondMenu.getParentId());
					if (menuLevel1 != null) {
						Set<Menu> menuLevel2 = menuLevel1.getSubMenu();
						for (Menu menu2 : menuLevel2) {
							if (menu2.getMenuId().equals(menu.getParentId())) {
								// 找到对应的二级菜单
								menu2.addSubMenu(menu);
								break;
							}
						}
					}

				}
			}
		}
		return new ArrayList<Menu>(menuMap.values());
	}
	
	/**
	 * 取第二级菜单
	 * @param menuId
	 * @return
	 */
	public Menu getParentMenu(Long menuId){
		Menu menu = menuMap.get(menuId);
		if(menu == null){
			return null;
		}
		if(menu.getGrade().equals(2)){
			return menu;
		}else{
			return getParentMenu(menu.getParentId());
		}
	}

	@Override
	public Menu getCurrentMenu(Long menuId) {
		return  menuMap.get(menuId);
	}
	
	/**
	 * 得到父节点
	 * @param menuList
	 * @param menu
	 * @return
	 */
	private Menu getParentMenu(Collection<Menu> menuList, Menu menu) {
		for (Menu item : menuList) {
			if (item.getMenuId().equals(menu.getParentId())) {
				return item;
			}
		}
		return null;
	}



}